﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Media3D;
using Microsoft.Win32;
using System.Threading.Tasks;
using DataProcessing;
using Gene.Render;
using System.Threading;
using System.Windows.Threading;
using System.IO;

namespace Gene
{
    /// <summary>
    /// Window1.xaml 的交互逻辑
    /// </summary>
    public partial class WindowMain:Window
    {
        
        public WindowMain()
        {
            
            InitializeComponent();
            _dragStart = NULL_POINT;
            InitGlobalContext();//用于显示鼠标信息
        }

        private void InitGlobalContext()
        {
            
            //AnnotationData.Ready = false;
            AppConfig tmp = AppConfig.Instance;
            Settings tmpSetting = Settings.Instance;
            ChromePairData.Data = null;
            _scene = Scene.NewInstance;
            _scene.Invalidate();
            _currentWindow = new ValueRange[Settings.Instance.ChromeCount];
            for (int k = 0; k < Settings.Instance.ChromeCount; k++)
                _currentWindow[k] = new ValueRange();
            SetupJustifyMethod();
        }

        //progressBar
        win_ProcessBar progBar;
        Thread updateOpenProgressThread;
        Thread getDataThread;
        
        const int MAX_ZOOM_RATIO = 12;
        Point _dragStart;
        Point NULL_POINT = new Point(int.MaxValue, int.MaxValue);



        Point3D _currMousePos;//屏幕坐标转化后所用

        string _helpText = "";
        int currChromeIndex = -1;

        /// <summary>
        /// species,preset
        /// </summary>
        void SetupDynamicContents()
        {
            SetupPresetMenus();
            SetupSpeciesMenu();
            UpdateDynamicTitle();
        }

        private void UpdateDynamicTitle()
        {
            this.Title = "Chrome Visualization\t" + ("(" + AppConfig.Instance.DefaultSpecieName + ")");
        }

        private void SetupSpeciesMenu()
        {
            return;
            //List<SpeciesSetting> setting = AppConfig.Instance.SpeciesSettings;
            //MenuItem parentMenu = this.ChooseSpeciesMenu;
            //parentMenu.Items.Clear();
            //for (int i = 0; i < setting.Count; i++)
            //{
            //    MenuItem item = new MenuItem();
            //    item.Header = setting[i].Name;
            //    item.Name ="Menu"+i;
            //    item.Click += new RoutedEventHandler(OnChooseSpecies_Item_Click);
            //    //item.IsCheckable = true;
                
            //    if (setting[i].Name.Equals(AppConfig.Instance.DefaultSpecieName, StringComparison.CurrentCultureIgnoreCase))
            //    {
            //        item.IsChecked = true;
            //    }
            //    parentMenu.Items.Add(item);
            //}
        }

        //void OnChooseSpecies_Item_Click(object sender, RoutedEventArgs e)
        //{

        //    if (ChromePairData.Data != null)
        //    {
        //        MessageBoxResult ret = MessageBox.Show("This will reset the data you are current viewing, continue?","Change Species Settings" ,MessageBoxButton.YesNo);
        //        if (ret != MessageBoxResult.Yes)
        //            return;

        //    }

        //    foreach (MenuItem subItem in this.ChooseSpeciesMenu.Items)
        //    {
        //        subItem.IsChecked = false;
        //    }

        //    MenuItem item = e.Source as MenuItem;
        //    AppConfig.Instance.DefaultSpecieName = item.Header.ToString();
        //    AppConfig.Instance.DefaultSpecie.Load();
        //    item.IsChecked = true;
        //    UpdateDynamicTitle();
        //    InitGlobalContext();
        //    InitializeModel();

        //}
        private void SetupPresetMenus()
        {
            List<PresetPoint> presets = AppConfig.Instance.PresetPoints;
            if (presets == null ||
                presets.Count == 0)
            {
                this.PresetPointsMenu.IsEnabled = false;
            }

            for (int i = 0; i < 4; i++)
            {
                MenuItem item = PresetPointsMenu.Items.GetItemAt(i) as MenuItem;
                bool outofRange = i >= presets.Count ? true : false;
                item.IsEnabled = !outofRange;
                if (!outofRange)
                    item.Header = presets[i].Name;
                else
                    item.Header = "not set";
            }
        }
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
           
            
            // Declare scene objects.
            // Defines the camera used to view the 3D object. In order to view the 3D object,
            // the camera must be positioned and pointed such that the object is within view 
            // of the camera.
           InitializeModel();
           int i;
           //progressBar_open.Visibility = Visibility.Hidden;

     
          
           SetupDynamicContents();
           textHelp.Text = "Move:Ctrl+Left button drag\n\n";
           textHelp.Text += "Zoom in/out: Double click Left/Right button\n\n";
           textHelp.Text += "View specific chrome data:Ctrl+Double click left button\n\n";
           textHelp.Text += "Change Roll:Left Button\n\n";
           textHelp.Text +="Change Pitch:Right Button\n\n";
           textHelp.Text+="Change Yaw: Alt+Left Button\n\n";
   
        }

        private void SetupJustifyMethod()
        {
            
            ComboAlignType.Items.Clear();

            this.ComboAlignType.Items.Add(ChromePairData.AlignMode.LeftJustify);
            if (Settings.Instance.HasAlighPoint())
            {
                 this.ComboAlignType.Items.Add(ChromePairData.AlignMode.CenterJustify);
            }
            else
            {
                ComboAlignType.IsEnabled = false;
            }
           
            this.ComboAlignType.SelectedIndex = 0;
        }
        Scene _scene;

      
        private void InitializeModel()
        {
            ViewPortPanel.Children.Clear();
            
            _scene.AddTo(ViewPortPanel.Children);
            _scene.Invalidate();
             
        }





  
 
        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
           


        }

        /// <summary>
        /// 这里需要修改。在读文件的进程里面修改这些比较好
        /// </summary>
        private void NotifyUpdate()
        {
            //ugly, but wait for file reading and parsing start
            System.Threading.Thread.Sleep(100);
            while (true)
            {

                double currValue = 0;
                double baseValue = BedFileParser.fileLength;
                {
                    if (BedFileParser.currReadPos < BedFileParser.fileLength)
                    {
                        currValue = BedFileParser.currReadPos;
                    }
                    else if (!BedFileParser._parsingBegin)
                    {
                        goto SleepLbl;
                    }
                    else if (BedFileParser.processedValue <= BedFileParser.bufferLen)
                    {
                        double parseProgress = BedFileParser.processedValue / BedFileParser.bufferLen;
                        currValue = baseValue + parseProgress * 4 * baseValue;//progressBar.maxium = 5*basevalue
                    }
                    else
                    {
                        goto SleepLbl;
                    }
                }
            
                progBar.progressBar1.Dispatcher.Invoke(DispatcherPriority.Render, new Action<double>(UpdateOpenProgress), currValue);
            SleepLbl:
                System.Threading.Thread.Sleep(50);

            }
        }
        private void UpdateOpenProgress(double currentLength)
        {
            if (progBar.closed)
            {
                getDataThread.Abort();
                updateOpenProgressThread.Abort();
                _loadCompleted = true;
                 this.ControlPanel.Dispatcher.Invoke(new Action(()=>
                {
                    this.ControlPanel.IsEnabled = true;
                }));
                
            }
            progBar.progressBar1.Value = currentLength;
            if (currentLength >= progBar.progressBar1.Maximum)
            {
                updateOpenProgressThread.Abort();
                progBar.Close();
                _loadCompleted = true;
            }
        }
        bool _loadCompleted = true;
  

        const int _currentZoomRatio = 1;
        const double _currentWindowIndex = 0;
     
 
         private void DrawScene()
         {
             int t1 = Environment.TickCount;
             ChromePairData.PrepareAllCurrentData();
             int t2 = Environment.TickCount;
             double elapse = (t2 - t1);
             _scene.DrawAllPlanes(ChromePairData.Data);
             int t3 = Environment.TickCount;
             int elapse2 = (t3 - t2);

             _scene.drawXYCoordinate(_currentWindow[0]);
             _scene.Invalidate();
         }

        ValueRange GetObservingWindow()
        {
            if (ChromePairData.Data == null)
                return new ValueRange { Start = 0, End = 0 };
            return ChromePairData.Data[0].CurrentWindow;
        }


        struct MouseButtonPushed
        {
            public bool leftButtonDown ;
            public bool rightButtonDown;

        }

        MouseButtonPushed _mouseStatus;

        /// <summary>
        /// Yaw:y, pitch:x, roll:z
        /// 实际上y,z相当于wpf的z,y.
        /// yaw：alt+左键
        /// pitch:右键
        /// z：左键
        /// 平移：ctrl+左键
        /// 放大：ctrl+右键双击
        /// 缩小：ctrl+左键双击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
         private void ViewPortPanel_MouseMove(object sender, MouseEventArgs e)
         {
             if (!_loadCompleted)
                 return;
             HandleControl(e);

             HandleHitTest();

             
         }

         private void HandleControl(MouseEventArgs e)
         {
             double deltaY = 0;
             double deltaX = 0;

             if (_dragStart == NULL_POINT)
                 return;
             //z:roll
             if (e.LeftButton == MouseButtonState.Pressed)
             {
                 deltaX = e.GetPosition(ViewPortPanel).X - _dragStart.X;
             }

             //x:pitch
             if (e.RightButton == MouseButtonState.Pressed)
             {
                 deltaY = e.GetPosition(ViewPortPanel).Y - _dragStart.Y;

             }
             _dragStart = e.GetPosition(ViewPortPanel);

             if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
             {
                 if (Util.IsnotZero(deltaX))
                     HandleMove(deltaX);

             }
             else
             {
                 if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt))
                 {
                     _scene.RotateZ(deltaX);
                 }
                 else
                 {
                     HandleRotate(deltaY, deltaX);
                 }
             }
         }


         void HandleMove(double dx)
         {
             if (ChromePairData.Data == null)
                 return;

             int pointPerPixel = (int)(_currentWindow[0].Size / ChromePairData.RESOLUTION);

             int valueOffset = (int)(-dx * pointPerPixel);

             ChromePairData data = ChromePairData.Data[0];
             ValueRange tmp = data.CurrentWindow.Clone();
             tmp = tmp.Add(valueOffset);
             tmp.Truncate(ChromePairData.AllWindowMax);

             if (tmp.Size <data.CurrentWindow.Size)//截断后导致大小变了，肯定是小了
             {
                 if (valueOffset > 0)
                 {
                     tmp.Start -= (data.CurrentWindow.Size - tmp.Size);
                 }
                 else
                 {
                     tmp.End += (data.CurrentWindow.Size - tmp.Size);
                 }

             }

            SetObservingWindow(tmp);

            DrawScene();
             
         }
         private void HandleRotate(double deltaY, double deltaX)
         {
            
      
             _scene.Rotate(deltaX, deltaY);
     
         }

         private void HandleHitTest()
         {
             Point mouseposition = Mouse.GetPosition(ViewPortPanel);//WPF方法

             Point3D testpoint3D = new Point3D(mouseposition.X, mouseposition.Y, 0);
             Vector3D testdirection = new Vector3D(mouseposition.X, mouseposition.Y, 10);
             PointHitTestParameters pointparams = new PointHitTestParameters(mouseposition);
             RayHitTestParameters rayparams = new RayHitTestParameters(testpoint3D, testdirection);
             //test for a result in the Viewport3D
             VisualTreeHelper.HitTest(ViewPortPanel, null, HTResult, pointparams);
         }


         private void ZoomOut(double ratio)
         {
             ValueRange newRange = _currentWindow[0].Scale(ratio,_coordinateInfo.SelectedValue );
             newRange.Truncate(ChromePairData.AllWindowMax);

             //不能无限扩大缩小
             if (newRange.Size < ChromePairData.RESOLUTION||
                 newRange.Size>ChromePairData.AllWindowMax.Size)
             {
                 return;
             }

             SetObservingWindow(newRange);
             DrawScene();
         }
         private void ZoomIn(double ratio)
         {
             ZoomOut(1/ratio);
         }



         private void SetObservingWindow(ValueRange obWindow)
         {
             int i = 0;

             //此处平移要保持窗口大小不变，
             foreach (ChromePairData data in ChromePairData.Data)
             {
                 data.CurrentWindow = obWindow;
                 _currentWindow[i++] = data.CurrentWindow;
             }
         }


         ValueRange[] _currentWindow;
         CoordinateDetector _coordinateInfo;
         public HitTestResultBehavior HTResult(System.Windows.Media.HitTestResult rawresult)
         {
             //MessageBox.Show(rawresult.ToString());
             RayHitTestResult rayResult = rawresult as RayHitTestResult;
             if (rayResult != null)
             {
          
                _coordinateInfo = new CoordinateDetector(rayResult.PointHit, _currentWindow);
                _coordinateInfo.GetMouseInfo();
                Chrome.Content = _coordinateInfo.SelectedChrome;
                ChromeRange.Content = _coordinateInfo.SelectedValue;

                currChromeIndex = _coordinateInfo.chrIndex;
                _currMousePos = rayResult.PointHit;
             }
             return HitTestResultBehavior.Continue;
         }
         
         private void btnReset_Click(object sender, RoutedEventArgs e)
         {
             if (!_loadCompleted)
                 return;
             _scene.ResetTransform();
         }

         private void Window_Closed(object sender, EventArgs e)
         {
             //Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
             Application.Current.Shutdown();
             Environment.Exit(0);
         }

         private void ViewPortPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
         {
             ViewPortPanel.Focus();
             _mouseStatus.leftButtonDown = true;
             _dragStart = e.GetPosition(ViewPortPanel);
             if (!_loadCompleted)
                 return;
             if (ChromePairData.Data == null)
             {
                 return;
             }
             if (e.ClickCount == 2)
             {
                 if (IsCtrlKeyDown())
                 {
                     win_2DPlane plane = new win_2DPlane(currChromeIndex);
                     AccountCaculationByPoint(_currMousePos.X, currChromeIndex);
                     plane.Draw(_zoom2DWindow);
                     plane.Show();
                 }
                 else
                 {
                     if (ChromePairData.Data == null)
                         return;
                     ZoomIn(ZOOM_STEP);
                 }
                 
             }
         }
         const double ZOOM_STEP = 1.5;
         bool IsCtrlKeyDown()
         {
             return Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
         }

         //为放大提供数据
         ValueRange _zoom2DWindow = new ValueRange();
         
        
         public void AccountCaculationByPoint(double x, int chromeIndex)
         {
             CoordinateDetector cd = new CoordinateDetector(_currentWindow);
             _zoom2DWindow = cd.GetRange(x, ChromePairData.Data[0].CurrentWindow);
             
          
         }

         bool _showChooseSpecies = true;
         private void OnOpenBedFile_Click(object sender, RoutedEventArgs e)
         {
             if (!_loadCompleted)
                 return;
             bool? ret = false;
             if (_showChooseSpecies)
             {
                 SpeciesSetting oldSetting = AppConfig.Instance.DefaultSpecie;
                 ChooseSpeciesWindow chooseSpecies = new ChooseSpeciesWindow();
                 ret = chooseSpecies.ShowDialog();
                 _showChooseSpecies = chooseSpecies._showMe;
                 if (!ret.Value)
                 {
                     return;
                 }
                 if (oldSetting != AppConfig.Instance.DefaultSpecie)
                 {
                     UpdateDynamicTitle();
                     InitGlobalContext();
                     InitializeModel();
                 }
         
             }
          
         
             OpenFileDialog ofd = new OpenFileDialog();
             ofd.Title = "Open Data File";
             ret = ofd.ShowDialog(this);
             if (ret.Value)
             {
                 this.ControlPanel.IsEnabled = false;
                 progBar = new win_ProcessBar();
                 progBar.progressBar1.Maximum = BedFileParser.GetFileLength(ofd.FileName)*5;
                 progBar.Show();
                 _loadCompleted = false;
                 _scene.drawProgress = 0;
                 getDataThread = new Thread(new ThreadStart(() =>
                 {
                     
                     ChromePairData.Data = BedFileParser.GetFromFile(ofd.FileName);

                     
                     ChromePairData.Initialize(true);
                     
                     for (uint i = 0; i < ChromePairData.Data.Length; i++)
                     {
                         _currentWindow[i] = ChromePairData.Data[i].CurrentWindow;
                     }
                     
                     ViewPortPanel.Dispatcher.Invoke(new Action(() =>
                     {
                         _scene.drawProgress = 8;

                         _scene.DrawAllPlanes(ChromePairData.Data);

                         _scene.drawXYCoordinate(GetObservingWindow());

                         progBar.Dispatcher.Invoke(DispatcherPriority.Render, new Action<double>(UpdateOpenProgress), progBar.progressBar1.Maximum);
                         _loadCompleted = true;

                     }));

                     System.GC.Collect();
                     this.ControlPanel.Dispatcher.Invoke(new Action(()=>
                         {
                             this.ControlPanel.IsEnabled = true;
                         }
                     ));

                 }));
                 getDataThread.IsBackground = true;
                 getDataThread.Start();
                 
                

                 updateOpenProgressThread = new Thread(new ThreadStart(NotifyUpdate));
                 updateOpenProgressThread.Priority = ThreadPriority.Normal;
                 updateOpenProgressThread.IsBackground = true;
                 updateOpenProgressThread.Start();
                 
             }
            
         }

         private void ComboAlignType_SelectionChanged(object sender, SelectionChangedEventArgs e)
         {
             //clear过后就是这个效果，得找到官方的方法来解决，而不是这样
             if (ComboAlignType.Items == null || ComboAlignType.Items.Count == 0)
                 return;

             ChromePairData.AlignMode selected = (ChromePairData.AlignMode)(ComboAlignType.SelectedValue);
             ChromePairData.Align = selected;
             if (ChromePairData.Data == null)
                 return;

             ChromePairData.BackToGlobalView();
             ChromePairData.Initialize();

             for (uint i = 0; i < ChromePairData.Data.Length; i++)
             {
                 _currentWindow[i] = ChromePairData.Data[i].CurrentWindow;
             }
             ChromePairData.PrepareAllCurrentData();
             _scene.DrawAllPlanes(ChromePairData.Data);
             _scene.drawXYCoordinate(GetObservingWindow());
             _scene.Invalidate();

         }

         private void OnOpenAnnotationFile_Click(object sender, RoutedEventArgs e)
         {
             OpenFileDialog ofd = new OpenFileDialog();
             bool? ret = ofd.ShowDialog(this);
             if (ret.Value)
             {
                 AnnotationData.Load(ofd.FileName);
             }
             
         }

    
         private void OnSavePos_Click(object sender, RoutedEventArgs e)
         {
             TransformMatrix mat = GraphicUtil.ToTransformMatrix(_scene.GetTransform().Value);
             AddPresetWindow window = new AddPresetWindow(mat);
             window.ShowDialog();
             SetupDynamicContents();
             GraphicUtil.SaveToImage(ViewPortPanel, @".\capture.bmp");
            
         }  
        private void OnGotoPresetPoint1(object sender, RoutedEventArgs e)
         {
             GotoPreset(0);
         }

         private void GotoPreset(int i)
         {
             Matrix3D matrix = GraphicUtil.ToMatrix3D(AppConfig.Instance.PresetPoints[i].Value);
             Transform3D trans = new MatrixTransform3D(matrix);

             _scene.TranformFromOrigin(trans);
         }
         private void OnGotoPresetPoint2(object sender, RoutedEventArgs e)
         {
             GotoPreset(1);
         }
         private void OnGotoPresetPoint3(object sender, RoutedEventArgs e)
         {
             GotoPreset(2);
         }
         private void OnGotoPresetPoint4(object sender, RoutedEventArgs e)
         {
             GotoPreset(3);
         }

         private void ViewPortPanel_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
         {
             ViewPortPanel.Focus();
             _mouseStatus.rightButtonDown = true;
             _dragStart = e.GetPosition(ViewPortPanel);
             if (ChromePairData.Data==null)
                 return;
             if (e.ClickCount == 2 )
             {
                 ZoomOut(ZOOM_STEP);
             }
            
         }

         private void ButtonScreenShot_Click(object sender, RoutedEventArgs e)
         {

             SaveFileDialog sfd = new SaveFileDialog();
             sfd.FileName = "capture.png";
             sfd.DefaultExt = "png";
             bool? ret = sfd.ShowDialog(this);
             if (ret.Value)
             {
                 GraphicUtil.SaveToImage(ViewPortPanel,sfd.FileName);
             }
         }

         private void ViewPortPanel_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
         {
             _mouseStatus.rightButtonDown = false;
             _dragStart = NULL_POINT;
         }

         private void ViewPortPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
         {
             _mouseStatus.leftButtonDown = false;
             _dragStart = NULL_POINT;
         }

      
         private void ViewPortPanel_KeyDown(object sender, KeyEventArgs e)
         {
             if (!_loadCompleted)
                 return;
             if (e.Key == Key.Left)
             {
                 _scene.Move(new Vector3D(-0.01, 0, 0));
             }
             else if (e.Key == Key.Right)
             {
                 //_scene.TurnLeftRight(1);
                 _scene.Move(new Vector3D(0.01, 0, 0));
             }
             else if (e.Key == Key.Up)
             {
                 _scene.Move(new Vector3D(0, 0.01, 0));
             }
             else if (e.Key == Key.Down)
             {
                 _scene.Move(new Vector3D(0, -0.01, 0));
             }
             _scene.Invalidate();
             e.Handled = true;
         }

         private void ViewPortPanel_KeyUp(object sender, KeyEventArgs e)
         {
             int a=0;
             a++;
         }

         const double SCALE_STEP = 1.1;
         private void buttonScaleUp_Click(object sender, RoutedEventArgs e)
         {
             _scene.Zoom(SCALE_STEP, ZOOM_CENTER);
         }
         static Point3D ZOOM_CENTER = new Point3D(0, 0, 0);
         private void buttonScaleDown_Click(object sender, RoutedEventArgs e)
         {
             _scene.Zoom(1/SCALE_STEP,ZOOM_CENTER);
         }

         private void ChooseSpeciesMenu_Click(object sender, RoutedEventArgs e)
         {
             if (!_loadCompleted)
                 return;
             bool? ret = false;
      
            SpeciesSetting oldSetting = AppConfig.Instance.DefaultSpecie;
            ChooseSpeciesWindow chooseSpecies = new ChooseSpeciesWindow();
            chooseSpecies.DisableVisibilityChoose();//不能让用户在此处选是否下次显示
            ret = chooseSpecies.ShowDialog();
          
            if (!ret.Value)
            {
                return;
            }
          
             if (oldSetting != AppConfig.Instance.DefaultSpecie)
            {
                UpdateDynamicTitle();
                InitGlobalContext();
                InitializeModel();
            }

          
         }
        
    }
}
